﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.Text.RegularExpressions;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Build.Client;

namespace TfsCloudDeploy.Workflow.Core.Editors
{
    public partial class BuildLabelsDialog : Form
    {
        public string AvailableBuildsPattern { get; set; }
        public int MaxNumberOfBuildLabelsToShow { get; set; }
        public bool ForceUseABuildLabel { get; set; }
        public VersionControlServer VersionControlServer { get; set; }
        public string BuildLabelSourceControlScope { get; set; }
        public string BuildLabelOwnerName { get; set; }
        public string TargetBuildLabel { get; set; }
        public bool ExcludeLabelsForFailedBuilds { get; set; }
        public string[] AcceptableBuildQualities { get; set; }
        public string TeamProjectName { get; set; }

        public BuildLabelsDialog()
        {
            InitializeComponent();
        }

        private void BuildLabelsDialog_Load(object sender, EventArgs e)
        {
            if (ForceUseABuildLabel)
            {
                NewBuildRadioButton.Checked = false;
                NewBuildRadioButton.Enabled = false;
                ExistingBuildRadioButton.Checked = true;
                ExistingBuildRadioButton.Enabled = true;
                BuildLabelsComboBox.Enabled = true;
            }
            else
            {
                NewBuildRadioButton.Checked = true;
                NewBuildRadioButton.Enabled = true;
                ExistingBuildRadioButton.Checked = false;
                ExistingBuildRadioButton.Enabled = true;
                BuildLabelsComboBox.Enabled = true;
            }

            PopulateComboWithBuildLabels();
        }

        private void OkButton_Click(object sender, EventArgs e)
        {
            if (NewBuildRadioButton.Checked)
            {
                TargetBuildLabel = VersionSpec.Latest.DisplayString; // "T"
            }
            else
            {
                if (BuildLabelsComboBox.Items.Count > 0 && BuildLabelsComboBox.SelectedIndex < 0)
                {
                    MessageBox.Show("A build label was not selected. The previous setting was kept.", "No Label Selected");
                }
                else
                {
                    TargetBuildLabel = (string)BuildLabelsComboBox.Items[BuildLabelsComboBox.SelectedIndex];
                }
            }

            Close();
        }

        private void CancelButton_Click(object sender, EventArgs e)
        {
            TargetBuildLabel = string.Empty;
            Close();
        }

        private void BuildLabelsComboBox_Enter(object sender, EventArgs e)
        {
            if (!ExistingBuildRadioButton.Checked)
            {
                ExistingBuildRadioButton.Checked = true;
                NewBuildRadioButton.Checked = false;
            }
        }

        private void PopulateComboWithBuildLabels()
        {
            VersionControlLabel[] availableLabels = VersionControlServer.QueryLabels(
                null,
                string.IsNullOrWhiteSpace(BuildLabelSourceControlScope) ? null : BuildLabelSourceControlScope,
                string.IsNullOrWhiteSpace(BuildLabelOwnerName) ? null : BuildLabelOwnerName,
                false);

            bool foundMatchingLabels = false;
            if (availableLabels.Length > 0 && MaxNumberOfBuildLabelsToShow > 0)
            {
                BuildLabelsComboBox.Items.Clear();

                IEnumerable<VersionControlLabel> filteredLabels = Enumerable.Empty<VersionControlLabel>();

                if (!string.IsNullOrWhiteSpace(AvailableBuildsPattern))
                {
                    // Filter the labels by the regex pattern:
                    filteredLabels = from label in availableLabels
                                     where Regex.IsMatch(label.Name, AvailableBuildsPattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline)
                                     select label;
                }
                else
                {
                    filteredLabels = availableLabels.AsEnumerable<VersionControlLabel>();
                }

                if (filteredLabels.Count() > 0)
                {
                    if (ExcludeLabelsForFailedBuilds || (AcceptableBuildQualities != null && AcceptableBuildQualities.Length > 0))
                    {
                        // Get the builds corresponding to the labels. Assumes that standard DefaultTemplate labeling takes place where the build label is the same as the build number.
                        TfsTeamProjectCollection tfsProjectCollection = VersionControlServer.TeamProjectCollection;
                        IBuildServer buildServer = tfsProjectCollection.GetService<IBuildServer>();
                        IBuildDetailSpec buildDetailSpec = buildServer.CreateBuildDetailSpec(TeamProjectName);
                        IBuildQueryResult buildQueryResult = buildServer.QueryBuilds(buildDetailSpec);

                        if (buildQueryResult.Builds.Length > 0)
                        {
                            // Add builds that do not match the conditions so they can be excluded.
                            // 1. Exclude failed builds if requested.
                            // 2. Filter by build qualities.
                            IEnumerable<IBuildDetail> exclusionBuildDetails = buildQueryResult.Builds.Where((build) =>
                            {
                                if (ExcludeLabelsForFailedBuilds && build.Status == BuildStatus.Failed)
                                    return true;
                                else if (AcceptableBuildQualities != null && AcceptableBuildQualities.Length > 0 && !AcceptableBuildQualities.Contains(build.Quality, StringComparer.InvariantCultureIgnoreCase))
                                    return true;

                                return false;
                            });

                            if (exclusionBuildDetails.Count() > 0)
                            {
                                // Only keep the labels that are not in the exclusion list.
                                filteredLabels = from label in filteredLabels
                                                 where !ContainsMatchingLabelInBuildDetails(exclusionBuildDetails, label.Name)
                                                 select label;
                            }
                        }
                        else
                        {
                            MessageBox.Show("No builds could be found that match the label criteria set in the 'Environment Settings' section of the build definition. Please review your settings and try again or continue without applying the build filters.", "No Builds Found");
                        }
                    }

                    if (filteredLabels.Count() > 0)
                    {
                        // Then sort descending by LastModifiedDate and take the last N.
                        IEnumerable<VersionControlLabel> sortedLabels = filteredLabels.OrderByDescending((label) => { return label.LastModifiedDate; }).Take(MaxNumberOfBuildLabelsToShow);

                        // Then populate the combo box.
                        Array.ForEach<VersionControlLabel>(sortedLabels.ToArray(), (label) => { BuildLabelsComboBox.Items.Add(label.Name); });

                        foundMatchingLabels = true;
                    }
                }
            }

            if (!foundMatchingLabels)
            {
                ExistingBuildRadioButton.Enabled = false;
                BuildLabelsComboBox.Enabled = false;
                OkButton.Enabled = !ForceUseABuildLabel;

                MessageBox.Show("No build labels could be found that match the criteria set in the 'Environment Settings' section of the build definition. Please review your settings and try again.", "No Labels Found");
            }
        }

        public bool ContainsMatchingLabelInBuildDetails(IEnumerable<IBuildDetail> exclusionBuildDetails, string labelName)
        {
            if (exclusionBuildDetails.Count() > 0)
            {
                foreach (var buildDetail in exclusionBuildDetails)
                {
                    if (string.Compare(buildDetail.BuildNumber, labelName, true) == 0)
                        return true;
                }
            }
            return false;
        }
    }
}
